#include "globals.h"
#include "errno.h"

#include "util/string.h"
#include "util/debug.h"

#include "mm/mmobj.h"
#include "mm/pframe.h"
#include "mm/mm.h"
#include "mm/page.h"
#include "mm/slab.h"
#include "mm/tlb.h"

int anon_count = 0; /* for debugging/verification purposes */

static slab_allocator_t *anon_allocator;

static void anon_ref(mmobj_t *o);
static void anon_put(mmobj_t *o);
static int  anon_lookuppage(mmobj_t *o, uint32_t pagenum, int forwrite, pframe_t **pf);
static int  anon_fillpage(mmobj_t *o, pframe_t *pf);
static int  anon_dirtypage(mmobj_t *o, pframe_t *pf);
static int  anon_cleanpage(mmobj_t *o, pframe_t *pf);

static mmobj_ops_t anon_mmobj_ops = {
        .ref = anon_ref,
        .put = anon_put,
        .lookuppage = anon_lookuppage,
        .fillpage  = anon_fillpage,
        .dirtypage = anon_dirtypage,
        .cleanpage = anon_cleanpage
};

/*
 * This function is called at boot time to initialize the
 * anonymous page sub system. Currently it only initializes the
 * anon_allocator object.
 */
void
anon_init()
{
/*        NOT_YET_IMPLEMENTED("VM: anon_init");*/
         anon_allocator = slab_allocator_create("anonymous", sizeof(mmobj_t));
         KASSERT(anon_allocator);
}

/*
 * You'll want to use the anon_allocator to allocate the mmobj to
 * return, then then initialize it. Take a look in mm/mmobj.h for
 * macros which can be of use here. Make sure your initial
 * reference count is correct.
 */
mmobj_t *
anon_create()
{
        /* NOT_YET_IMPLEMENTED("VM: anon_create");*/
        mmobj_t* newMmobj = (mmobj_t*)slab_obj_alloc(anon_allocator);
        mmobj_init(newMmobj, &anon_mmobj_ops);
        newMmobj->mmo_refcount++;
        return newMmobj;
}

/* Implementation of mmobj entry points: */

/*
 * Increment the reference count on the object.
 */
static void
anon_ref(mmobj_t *o)
{
/*        NOT_YET_IMPLEMENTED("VM: anon_ref");*/
        KASSERT(o && (0 < o->mmo_refcount) && (&anon_mmobj_ops == o->mmo_ops));
        o->mmo_refcount++;
}

/*
 * Decrement the reference count on the object. If, however, the
 * reference count on the object reaches the number of resident
 * pages of the object, we can conclude that the object is no
 * longer in use and, since it is an anonymous object, it will
 * never be used again. You should unpin and uncache all of the
 * object's pages and then free the object itself.
 */
static void
anon_put(mmobj_t *o)
{
/*        NOT_YET_IMPLEMENTED("VM: anon_put");*/
        KASSERT(o && (0 < o->mmo_refcount) && (&anon_mmobj_ops == o->mmo_ops));

        o->mmo_refcount--;
        if(o->mmo_nrespages==o->mmo_refcount)
        {
                pframe_t *pf = NULL;
               list_iterate_begin(&o->mmo_respages, pf, pframe_t, pf_olink) { 
                                        /* how to uncache?*/
                            pframe_unpin(pf);   /*we dont need to unpin the pframe manually*/
                            list_remove(&pf->pf_olink);
                            list_remove(&pf->pf_hlink);
/*                            hashchain = &pframe_hash[hash_page(o, pf->pf_pagenum)];*/
                }list_iterate_end();

                slab_obj_free(anon_allocator,o); /*free itself*/
        }
}

/* Get the corresponding page from the mmobj. No special handling is
 * required. */
static int
anon_lookuppage(mmobj_t *o, uint32_t pagenum, int forwrite, pframe_t **pf)
{
/*        NOT_YET_IMPLEMENTED("VM: anon_lookuppage");*/
        return pframe_get(o, pagenum, pf);
}

/* The following three functions should not be difficult. */
        /* Fill the page frame starting at address vp->vp_paddr with the
         * contents of the page identified by vp->vp_obj and vp->vp_pagenum.
         * This may block.
         * Return 0 on success and -errno otherwise.
         */

static int
anon_fillpage(mmobj_t *o, pframe_t *pf)
{
/*        NOT_YET_IMPLEMENTED("VM: anon_fillpage");*/

         memset(pf->pf_addr, 0, PAGE_SIZE);
         return 0;
        /* KASSERT(pframe_is_busy(pf));
         KASSERT(!pframe_is_pinned(pf));
       
        /*how to fill a page with 0s will pframe_get return a page with 0s?
         pframe_t * new_pframe=NULL;
        if(pframe_get(o,pf->pf_pagenum,&new_pframe)==0)
        {
              memset(new_pframe->pf_addr, 0, PAGE_SIZE);
              return 0;
         }
         return -1;*/
}

static int
anon_dirtypage(mmobj_t *o, pframe_t *pf)
{
      /*do not need to implement for anno*/
/*        NOT_YET_IMPLEMENTED("VM: anon_dirtypage");*/
        return 0;
}

static int
anon_cleanpage(mmobj_t *o, pframe_t *pf)
{
/*        NOT_YET_IMPLEMENTED("VM: anon_cleanpage");*/
     /*do not need to implement for anno*/
        return 0;
}
